cmake_minimum_required(VERSION 3.20)

project(trace-ext4 CXX)
set(CMAKE_CXX_STANDARD 17)

if(NOT DEFINED BPFTOOL_PATH)
        set(BPFTOOL_PATH "bpftool")
endif()

if(NOT DEFINED CLANG_PATH)
        set(CLANG_PATH "clang")
endif()

add_custom_command(
  OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/vmlinux.h
  COMMAND bpftool btf dump file /sys/kernel/btf/vmlinux format c > ${CMAKE_CURRENT_BINARY_DIR}/vmlinux.h
  DEPENDS /sys/kernel/btf/vmlinux
)

function(generate_bpf_skel bpf_source bpf_headers bpf_object skel_output)
        add_custom_command(OUTPUT ${bpf_object}
                COMMAND ${CLANG_PATH} -g -O2 -target bpf -D__TARGET_ARCH_x86
                                -I ${CMAKE_CURRENT_BINARY_DIR}
                                -c ${bpf_source}
                                -o ${bpf_object}
                COMMAND ${LLVM_STRIP_PATH} -g ${bpf_object}
                MAIN_DEPENDENCY ${bpf_source}
                DEPENDS ${bpf_headers} ${CMAKE_CURRENT_BINARY_DIR}/vmlinux.h
        )
        add_custom_command(OUTPUT ${skel_output}
                COMMAND ${BPFTOOL_PATH} gen skeleton ${bpf_object} > ${skel_output}
                VERBATIM
                MAIN_DEPENDENCY ${bpf_object}
        )
endfunction(generate_bpf_skel)

function(add_core_executable)
        set(ONE_VALUE NAME)
        set(MULTI_VALUE SRC BPF_SRC BPF_HEADERS LIBRARIES)
        set(_TEMP_SKEL "")
        set(_BPF_DIR "")
        cmake_parse_arguments(CORE_EXECUTABLE "" "${ONE_VALUE}" "${MULTI_VALUE}" ${ARGN})
        list(TRANSFORM ${CORE_EXECUTABLE_BPF_HEADERS} PREPEND ${CMAKE_CURRENT_SOURCE_DIR})
        foreach(bpf_src IN ITEMS ${CORE_EXECUTABLE_BPF_SRC})
                # Extract the directory part of the source file, such as "src"
                get_filename_component(bpf_src_dir ${bpf_src} DIRECTORY)

                set(build_dir "${CMAKE_BINARY_DIR}/${bpf_src_dir}")
                file(MAKE_DIRECTORY ${build_dir})

                list(FIND _BPF_DIR ${CMAKE_CURRENT_BINARY_DIR}/${bpf_src_dir} index)
                if(index EQUAL -1)
                        list(APPEND _BPF_DIR ${CMAKE_CURRENT_BINARY_DIR}/${bpf_src_dir})
                endif()

                string(REGEX REPLACE "\.bpf\.c$" ".bpf.o" temp_bpf_object ${bpf_src})
                string(REGEX REPLACE "\.bpf\.c$" ".bpf.skel.h" bpf_skel ${bpf_src})
                set(_skel_output "${CMAKE_CURRENT_BINARY_DIR}/${bpf_skel}")
                generate_bpf_skel(
                        "${CMAKE_CURRENT_SOURCE_DIR}/${bpf_src}"
                        "${CORE_EXECUTABLE_BPF_HEADERS}"
                        "${CMAKE_CURRENT_BINARY_DIR}/${temp_bpf_object}"
                        "${_skel_output}"
                )
                list(APPEND _TEMP_SKEL "${_skel_output}")
        endforeach()
        add_executable(${CORE_EXECUTABLE_NAME} ${CORE_EXECUTABLE_SRC} ${_TEMP_SKEL})
        target_include_directories(${CORE_EXECUTABLE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${_BPF_DIR})
        target_link_libraries(${CORE_EXECUTABLE_NAME} PRIVATE ${CORE_EXECUTABLE_LIBRARIES})
        install(TARGETS ${CORE_EXECUTABLE_NAME} RUNTIME DESTINATION bin)
endfunction(add_core_executable)

add_core_executable(
    NAME capsnoop
    SRC src/capsnoop.cpp
    BPF_SRC src/cap.bpf.c
    BPF_HEADERS src/cap.bpf.h
    LIBRARIES bpf stdc++fs bcc
)

install(FILES man/man1/capsnoop.1.gz DESTINATION /usr/share/man/man1)
install(FILES man/zh_CN/man1/capsnoop.1.gz DESTINATION /usr/share/man/zh_CN/man1)